package br.unifor.bd.deadlock;

import java.util.Iterator;
import java.util.Vector;

import br.unifor.bd.protocol.Escalonador;
import br.unifor.bd.protocol.Transacao;

public class WaitForGrafo extends PrevencaoDeDeadlock {
	
	class Aresta {
		private final Transacao origem;
		private final Transacao destino;
		
		public Aresta(Transacao de, Transacao para) {
			this.origem = de;
			this.destino = para;
		}

		public boolean ehIgualA(Aresta aresta) {
			return origem == aresta.destino && destino == aresta.origem;
		}
		
		public String getIdDaTransacaoDeOrigem() {
			return origem.getId();
		}
		
		public String getIdDaTransacaoDeDestino() {
			return destino.getId();
		}

		public Transacao getOrigem() {
			return origem;
		}
 		
	}
	
	private Vector<Aresta> arestas = new Vector<Aresta>();
	
	private String status;
	
	private final Escalonador escalonador;
	
	public WaitForGrafo(Escalonador escalonador) {
		this.escalonador = escalonador;
	}

	public void adicionarAresta(Transacao transacao1, Transacao transacao2) {
		Aresta aresta = new Aresta(transacao1, transacao2);
		arestas.add(aresta);
	}
	
	public void removerAresta(Transacao transacao) {
		Vector<Aresta> temp = new Vector<WaitForGrafo.Aresta>();
		Iterator<Aresta> arestas = this.arestas.iterator();
		
		while (arestas.hasNext()) {
			Aresta aresta = arestas.next();
			
			if (aresta.getIdDaTransacaoDeOrigem() != transacao.getId()
					&& aresta.getIdDaTransacaoDeDestino() != transacao.getId()) {
				temp.add(aresta);
			}
		}
		
		this.arestas = temp;
	}
	
	public boolean temCiclo() {
		Iterator<Aresta> iterator = arestas.iterator();
		
		while (iterator.hasNext()) {
			Aresta aresta = iterator.next();
			
			Iterator<Aresta> iterator2 = arestas.iterator();
			while (iterator2.hasNext()) {
				Aresta aresta2 = iterator2.next();
				
				if (aresta.ehIgualA(aresta2)) {
					status = aresta.getIdDaTransacaoDeOrigem() + " esperando por " + aresta.getIdDaTransacaoDeDestino();
					status += "e " + aresta2.getIdDaTransacaoDeOrigem() + " esperando por " + aresta2.getIdDaTransacaoDeDestino();
					
					return true;
				}
			}
		}
		return false;
	}

	public String getStatus() {
		return status;
	}

	@Override
	public boolean detectar(Transacao transacao1, Transacao transacao2) {
		adicionarAresta(transacao1, transacao2);
		
		if (temCiclo()) {
			Transacao transacaoParaAbortar = transacao1;
			if (transacao1.ehAntesDaTransacao(transacao2)) {
				transacaoParaAbortar = transacao2;
			} 
			
			escalonador.abortarTransacao(transacaoParaAbortar);
			return true;
		}
		
		return false;
	}

	@Override
	public String retrievePreventType() {
		return "WFG";
	}
	
}
